﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Documents;
using CPPEI.Coolzon.ServiceProxy;
using CPPEI.Coolzon.Metadata;
using System.IO;
using CPPEI.Coolzon.Common;
using CPPEI.Coolzon.UI.XProperty.Converters;
using System.Threading;
using System.Windows.Media.Effects;
using CPPEI.Coolzon.UI;
using CPPEI.Coolzon.UI.Views;
using CPPEI.Coolzon.Event.NCL;
using Sunboxsoft.ChanceData.ServiceProxy.SyncServiceRef;
using Sunboxsoft.ChanceData.ServiceProxy.MainServiceRef;
using Sunboxsoft.ChanceData.ServiceProxy.UpdateServiceRef;

namespace Coolvis.Console.Theme
{
    /// <summary>
    /// Interaction logic for SyncView.xaml
    /// 同步配置信息的视图
    /// </summary>
    public partial class GreenLoadingView : View, ILoadingView
    {
        /// <summary>
        /// 加载回来的方案信息
        /// </summary>
        public Solution LoadedSolution { get; private set; }

        /// <summary>
        /// 方案的名称
        /// </summary>
        public string SolutionName { get; set; }

        /// <summary>
        ///  方案文件存放的路径
        /// </summary>
        public string SolutionFilePath { get; set; }

        /// <summary>
        ///  方案存放目录的路径
        /// </summary>
        public string SolutionDirectoryPath { get; set; }

        /// <summary>
        /// 总共的加载步骤
        /// </summary>
        private int _totalLoadStep = 5;

        private MainServiceClient msc;
        private UpdateServiceClient usc;

        // 需要预加载的场景
        private Stack<Scene> _sceneMetadataStack;

        public event CPPEI.Coolzon.UI.Views.LoadingView.LoadSolutionError LoadSolutionErrorEvent;

        /// <summary>
        /// 所有的数据组件
        /// </summary>
        private List<IDataPart> _allDataPartList;

        private System.Threading.Thread _thread;

        public GreenLoadingView(string solution, string solutionFile, string solutionDirectory)
        {
            InitializeComponent();

            SolutionName = solution;
            SolutionFilePath = solutionFile;
            SolutionDirectoryPath = solutionDirectory;

            UIContext.Current.IsLoadingSolution = true;
            UIContext.Current.OpenedSceneList = new Stack<string>();
        }

        protected override void OnActive()
        {
            _ctlTxtBckVersion.Text = string.Format("版本号:v{0}", UIContext.Current.AppVersion);

            //GC.Collect();
            if (IsFirstActive)
            {
                // 注册到事件总线
                EventBus.Create(ClientProxyManager.EVENTBUS_FLAGNAME).Register(this);

                _thread = new System.Threading.Thread(Run);
                _thread.Name = "LoadingViewLoadingThread";
                _thread.Start();
            }
        }

        protected override void OnDestory()
        {
            Logger.DebugToTag("UI", "loadingview destory!");
            _thread.Abort();
        }

        /// <summary>
        /// 加载线程
        /// </summary>
        private void Run()
        {
            // 通知服务器终端在进行同步
            NCLUpdateClientStatusEvent @eventLoading = new NCLUpdateClientStatusEvent(CPPEI.Coolzon.Service.Contract.ClientStatus.Loading);
            NCLEventBus.PublishAsync(@eventLoading);

            PreloadModule.Release();

            bool isSuccess = true;
            SyncSchedule syncSchedule = null;
            string outMessage;

            // 如果有打开的方案信息，需要再这里释放占用的资源
            // 回收资源
            if (UIContext.Current.RenderView != null)
            {
                // 1.去除RenderView里的所有组件的引用
                this.Dispatcher.Invoke(() =>
                {
                    (UIContext.Current.RenderView as RenderView).ClearAllParts();
                });
                // 2.去除组件创建缓存中的组件引用
                PartInstanceCache.ClearAllParts();
                // 3.清除资源缓存
                CPPEI.Coolzon.UI.XProperty.XPropertyDescriptionCache.Instance.Clear();
            }

            #region step1:加载本地方案
            OnMainProcessUpdate(_totalLoadStep, 0, "加载本地方案");
            LoadSolution(out outMessage);
            OnMainProcessUpdate(_totalLoadStep, 1, "加载本地方案完成");
            #endregion

            OnMainProcessUpdate(_totalLoadStep, 1, "验证方案版本中");
            SyncServiceProxyManager.Init();
            //step2:与服务器验证方案，检查方案是否需要更新
            if (!VerifySolution(out syncSchedule, out outMessage))
            {
                // 验证出错
                SyncServiceProxyManager.Release();

                LoadError(outMessage);
            }
            else
            {
                OnMainProcessUpdate(_totalLoadStep, 2, "验证方案完成");

                #region step3:如果需要更新，则更新方案,重新加载本地方案;如果不需要更新则不更新方案
                if (syncSchedule.IsNeedSync)
                {
                    //方案需要同步
                    OnMainProcessUpdate(_totalLoadStep, 2, "同步方案中");
                    //同步方案
                    if (!SyncSolution(syncSchedule, out outMessage))
                    {
                        // 同步出错
                        SyncServiceProxyManager.Release();

                        LoadError(outMessage);
                        return;
                    }

                    // 同步完成之后重新加载方案
                    if (!LoadSolution(out outMessage))
                    {
                        LoadError(outMessage);
                        return;
                    }
                    OnMainProcessUpdate(_totalLoadStep, 3, "同步方案完成");
                }
                else
                {
                    //方案不需要同步
                    OnMainProcessUpdate(_totalLoadStep, 3, "方案不需要更新");
                }
                SyncServiceProxyManager.Release();
                #endregion

                #region step4:初始化UIObject

                OnMainProcessUpdate(_totalLoadStep, 3, "初始化UI对象");

                InitUIObject();
                OnMainProcessUpdate(_totalLoadStep, 4, "初始化UI对象完成");
                #endregion

                #region step5:完成
                OnMainProcessUpdate(_totalLoadStep, 5, "完成");
                Finished();
                #endregion
            }
        }

        /// <summary>
        /// 加载方案出错时发布出错事件
        /// </summary>
        /// <param name="errorMessage">错误信息</param>
        private void LoadError(string errorMessage)
        {
            UIContext.Current.IsLoadingSolution = false;
            this.Dispatcher.Invoke(delegate
            {
                //通知服务器已恢复正常状态
                NCLUpdateClientStatusEvent @eventNormal = new NCLUpdateClientStatusEvent(CPPEI.Coolzon.Service.Contract.ClientStatus.Normal);
                NCLEventBus.PublishAsync(@eventNormal);
                if (LoadSolutionErrorEvent != null)
                {
                    LoadSolutionErrorEvent(errorMessage);
                }
            });
        }

        #region step1:验证方案版本
        /// <summary>
        /// 
        /// </summary>
        /// <param name="isNeedSync"></param>
        /// <param name="outMessage"></param>
        /// <returns></returns>
        private bool VerifySolution(out SyncSchedule syncSchedule, out string outMessage)
        {
            outMessage = string.Empty;
            syncSchedule = null;

            SyncServiceProxy ssp = SyncServiceProxyManager.GetAvailableProxy();
            if (ssp == null)
            {
                outMessage = "验证方案失败，网络错误!";

                OnSubProcessUpdate(1, 1, "验证方案失败，网络错误");
                return false;
            }

            // 获取版本
            string localVersion = string.Empty;
            if (this.LoadedSolution != null)
            {
                localVersion = LoadedSolution.Version.ToString();
            }

            // 获取同步计划
            try
            {
                syncSchedule = ssp.Client.GetSyncSchedule(SolutionName, localVersion);
                if(syncSchedule == null)
                {
                    outMessage = "验证方案失败，网络错误!";
                    OnSubProcessUpdate(1, 1, "验证方案失败，服务器错误");
                    return false;
                }
            }
            catch (Exception ex)
            {
                SyncServiceProxyManager.Error(ssp);
                outMessage = "验证方案失败，网络错误!";
                OnSubProcessUpdate(1, 1, "验证方案失败，网络错误");
                syncSchedule = null;
                return false;
            }

            return true;
        }
        #endregion

        #region step2:同步文件
        /// <summary>
        /// 同步方案
        /// 1.获取本地已有的文件列表
        /// 2.比较本地和服务器的列表，去除本地过多的文件记录，并删除文件
        /// 3.从服务器记录下载文件
        /// </summary>
        private bool SyncSolution(SyncSchedule syncSchedule, out string outMessage)
        {
            outMessage = "";

            List<SyncFile> syncFiles = CompareSFVL(syncSchedule, GetSolutionSFVL());


            if (syncFiles == null || syncFiles.Count <= 0)
            {
                outMessage = "获取同步计划完成,没有文件需要同步";

                OnSubProcessUpdate(1, 1, "没有文件需要同步");
                return true;
            }

            int totalProgress = syncFiles.Count + 1;

            int fileIndex = 0;
            foreach (var syncFile in syncFiles)
            {
                OnSubProcessUpdate(totalProgress, ++fileIndex, syncFile.Name);
                if (!SyncFile(syncFile, out outMessage))
                {
                    OnSubProcessUpdate(syncFiles.Count, fileIndex, outMessage);
                    return false;
                }
            }

            OnSubProcessUpdate(totalProgress, totalProgress, "正在保存验证文件");
            string sfvlName = string.Format("{0}\\{1}.sfvl", this.SolutionDirectoryPath, this.SolutionName);
            SolutionPackage solutionPackage = FileMD5Helper.GetSolutionPackage(this.SolutionDirectoryPath);
            FileMD5Helper.SerializeToFile(sfvlName, solutionPackage);

            OnSubProcessUpdate(totalProgress, totalProgress, "保存验证文件完成");

            return true;
        }

        private SolutionPackage GetSolutionSFVL()
        {
            string sfvlName = string.Format("{0}\\{1}.sfvl", this.SolutionDirectoryPath, this.SolutionName);

            if (this.LoadedSolution != null && !File.Exists(sfvlName))
            {
                SolutionPackage solutionPackage = FileMD5Helper.GetSolutionPackage(this.SolutionDirectoryPath);
                FileMD5Helper.SerializeToFile(sfvlName, solutionPackage);
                return solutionPackage;
            }

            return FileMD5Helper.Deserialize<SolutionPackage>(sfvlName);
        }

        private List<SyncFile> CompareSFVL(SyncSchedule syncSchedule, SolutionPackage solutionPackage)
        {
            if (solutionPackage == null)
            {
                return syncSchedule.Files.ToList();
            }
            string outMessage = "";
            List<SyncFile> sf_Server = syncSchedule.Files.ToList();
            List<SolutionFile> sf_Local = solutionPackage.Files.ToList();
            
            for(int index =0; index < sf_Local.Count;)
            {
                bool isExist = false;
                for(int j =0; j < sf_Server.Count; j++)
                {
                    if(string.Equals(sf_Local[index].FileName, sf_Server[j].Name))
                    {
                        isExist = true;
                        break;
                    }
                }
                if (!isExist)
                {
                    DeleteFile(this.SolutionDirectoryPath + sf_Local[index].FileName, ref outMessage);
                    sf_Local.RemoveAt(index);
                }
                else
                {
                    index++;
                }
            }
            List<SyncFile> synvFileList = new List<SyncFile>();
            for (int index = 0; index < sf_Server.Count; index++)
            {
                bool isNeedDown = true;
                for (int j = 0; j < sf_Local.Count; j++)
                {
                    if (string.Equals(sf_Server[index].Name, sf_Local[j].FileName))
                    {
                        if (string.Equals(sf_Server[index].MD5, sf_Local[j].MD5) && File.Exists(this.SolutionDirectoryPath + sf_Server[index].Name))
                        {
                            isNeedDown = false;
                        }
                        sf_Local.RemoveAt(j);
                        break;
                    }
                }
                if (isNeedDown)
                {
                    synvFileList.Add(sf_Server[index]);
                }
            }

            return synvFileList;
        }

        /// <summary>
        /// 下载单个文件
        /// </summary>
        /// <returns></returns>
        private bool SyncFile(SyncFile syncFile, out string outMessage)
        {
            Logger.InfoToTag("UI", string.Format("同步文件:{0}", syncFile.Name));

            outMessage = "";
            long downloadByteSize = 0;
            long beginTicks = DateTime.Now.Ticks;
            double downloadSpeed = 0;
            long offset = 0;
            bool isNeedLoad = true;
            string tempFileName = string.Format("{0}.tmp", Guid.NewGuid().ToString("N"));

            string realFileFullName = string.Format("{0}\\{1}", SolutionDirectoryPath, syncFile.Name);
            if (!DeleteFile(realFileFullName, ref outMessage))
            {
                return false;
            }

            SyncServiceProxy ssp = SyncServiceProxyManager.GetAvailableProxy();
            SyncResult syncResult = null;
            while (isNeedLoad)
            {
                try
                {
                    syncResult = ssp.Client.SyncFile(SolutionName, syncFile.Name, offset);
                }
                catch (Exception ex)
                {
                    Logger.ErrorToTag("UI", "从服务器获取文件:{0}失败,重新获取Proxy对象,ex:{1}", syncFile.Name, ex);

                    SyncServiceProxyManager.Error(ssp);
                    syncResult = null;
                }

                if (syncResult!= null && syncResult.Success)
                {
                    downloadByteSize += syncResult.Data.Length;
                    string tempFileFullName = string.Format("{0}\\{1}", SolutionDirectoryPath, tempFileName);
                    WriteTempFile(tempFileFullName, syncResult);
                    offset += syncResult.Data.Length;

                    Logger.InfoToTag("UI", string.Format("同步文件:{0},{1}/{2}", syncFile.Name, offset, syncFile.Size));

                    if (offset == syncFile.Size)
                    {

                        Logger.InfoToTag("UI", string.Format("同步文件:{0},完毕移动文件", syncFile.Name));

                        try
                        {
                            FileInfo tempFileInfo = new FileInfo(tempFileFullName);
                            tempFileInfo.MoveTo(realFileFullName);
                        }
                        catch(Exception ex)
                        {
                            Logger.ErrorToTag("UI", string.Format("同步文件:{0},移动文件出错:{1}", syncFile.Name, ex));

                            outMessage = string.Format("同步文件：{0}，出错,覆盖失败！", syncFile.Name);
                            return false;
                        }
                        isNeedLoad = false;
                    }
                    else
                    {
                        if ((DateTime.Now.Ticks - beginTicks) >= TimeSpan.FromSeconds(1).Ticks)
                        {
                            downloadSpeed = (double)downloadByteSize / TimeSpan.FromTicks(DateTime.Now.Ticks - beginTicks).Seconds / 1024.0d;
                            downloadByteSize = 0;
                            beginTicks = DateTime.Now.Ticks;
                        }
                        else
                        {
                            downloadSpeed = downloadByteSize / 1024.0d; ;
                        }
                        isNeedLoad = true;
                    }
                    OnSubProcessMessageUpdate(string.Format("{0}({1:N0}%)", syncFile.Name, ((double)offset / (double)syncFile.Size) * 100));
                }
                else
                {
                    ssp = SyncServiceProxyManager.GetAvailableProxy();
                    if (ssp == null)
                    {
                        Logger.ErrorToTag("UI", "同步文件：{0}，出错,无可用服务",
                            syncFile.Name);

                        outMessage = string.Format("同步文件：{0}，出错,无可用服务", syncFile.Name);
                        return false;
                    }
                }
            }

            return true;
        }

        private void WriteTempFile(string tempFilePath, SyncResult syncResult)
        {
            using (FileStream fileStream = new FileStream(tempFilePath, FileMode.Append))
            {
                fileStream.Write(syncResult.Data, 0, syncResult.Data.Length);
            }
        }

        private bool DeleteFile(string fileName, ref string outMessage)
        {
            FileInfo fileInfo = new FileInfo(fileName);
            if (!fileInfo.Directory.Exists)
            {
                fileInfo.Directory.Create();
            }

            if (File.Exists(fileName))
            {
                try
                {
                    File.Delete(fileName);
                    return true;
                }
                catch (Exception ex)
                {
                    Logger.ErrorToTag("UI", "同步文件时，已存在文件：{0}，删除文件失败:{1}",
                        fileName,
                        ex);

                    outMessage = string.Format("同步文件时，已存在文件：{0}，删除文件失败:{1}", fileName, ex.Message);

                    return false;
                }
            }
            return true;
        }
        #endregion

        #region step2:加载方案配置

        private bool LoadSolution(out string outMessage)
        {
            try
            {

            Thread.Sleep(100);
            outMessage = "";

            LoadedSolution = SolutionXmlProxy.Load(SolutionFilePath);
            if (LoadedSolution == null || LoadedSolution.Scenes == null)
            {
                OnSubProcessUpdate(1, 1, "加载方案出错");
                outMessage = "加载方案出错!";
                return false;
            }
            else
            {
                OnSubProcessUpdate(1, 1, "加载方案完毕");
                outMessage = "加载方案完毕";

                return true;
                }
            }
            catch(Exception ex)
            {
                OnSubProcessUpdate(1, 1, "加载方案出错");
                outMessage = "加载方案出错!";
                Logger.ErrorToTag("UI", "加载方案出错：{0}", ex);
                return false;
            }
        }

        #endregion

        #region step3:初始化UI对象
        private void InitUIObject()
        {
            Thread.Sleep(100);
            #region 初始化UIContext
            Logger.DebugToTag("UI", "初始化UIContext");
            UIContext.Current.Solution = LoadedSolution;
            UIContext.Current.SetSolutionName(LoadedSolution.Name);
            UIContext.Current.SolutionDirectory = SolutionDirectoryPath;
            if (LoadedSolution.Properties != null)
            {
                for (int index = 0; index < LoadedSolution.Properties.Count; index++)
                {
                    Property property = LoadedSolution.Properties[index];
                    if (property.Name.ToUpper() == "WIDTH")
                    {
                        UIContext.Current.SolutionWidth = Convert.ToDouble(property.ValueString);
                    }
                    else if (property.Name.ToUpper() == "HEIGHT")
                    {
                        UIContext.Current.SolutionHeight = Convert.ToDouble(property.ValueString);
                    }
                }
            }

            ConvertContext convertContext = new ConvertContext();
            convertContext.SolutionName = UIContext.Current.SolutionName;
            convertContext.SolutionDirectory = UIContext.Current.SolutionDirectory;
            PartInstanceCache.SetConvertContext(convertContext);

            // 初始化方案资源管理器
            SolutionResourceManager.Init(UIContext.Current.SolutionDirectory);

            Logger.DebugToTag("UI", "创建RenderView");
            this.Dispatcher.Invoke(() =>
            {
                try
                {
                    UIContext.Current.RenderView = new RenderView(LoadedSolution);
                }
                catch (Exception ex)
                {
                    Logger.ErrorToTag("UI", "创建RenderView失败,ex;{0}", ex);
                    return;
                }
                PartInstanceCache.Reset();
            });
            #endregion
        }
        #endregion

        #region step5:完成,检测预加载
        private void Finished()
        {
            Thread.Sleep(100);

            PreloadModule.Process(LoadedSolution);

            // 发布加载完毕的事件
            EventBus.Instance.Publish(new SolutionLoadingFinishedEvent());

            this.Dispatcher.Invoke(() => { Submit(); });
        }
        #endregion

        /// <summary>
        /// 主进度更新
        /// </summary>
        /// <param name="totalStep"></param>
        /// <param name="currentStep"></param>
        /// <param name="message"></param>
        private void OnMainProcessUpdate(int totalStep, int currentStep, string message)
        {
            Logger.DebugToTag("UI", message);
            if (!this.Dispatcher.HasShutdownStarted
                && !this.Dispatcher.HasShutdownFinished)
            {
                this.Dispatcher.Invoke(() =>
                {
                    if (UIContext.Current.AppType == "DisplayApp")
                    {
                        _ctlTxtBckName.Visibility = System.Windows.Visibility.Hidden;
                        _ctlTxtBckVersion.Visibility = System.Windows.Visibility.Hidden;
                        _ctlTxtBckUnit.Visibility = System.Windows.Visibility.Hidden;
                    }
                    //_ctlLblMessage.Text = string.Format("{0},{1}/{2}", message, currentStep, totalStep);
                    BlurEffect logoBlurEffect = _ctlLogo.Effect as BlurEffect;
                    logoBlurEffect.Radius = (Convert.ToDouble(currentStep) / Convert.ToDouble(totalStep)) * 50;
                    _ctlTxtBck.Text = string.Format("{0:N0}%", (Convert.ToDouble(currentStep) / Convert.ToDouble(totalStep)) * 100);

                    _ctllblMainStep.Content = string.Format("{0}|{1}", currentStep, totalStep);
                    _ctlMainSP.Width = (1 - Convert.ToDouble(currentStep) / Convert.ToDouble(totalStep)) * _ctlStackPanel.Width;
                    _ctlCanvas.Margin = new Thickness(_ctlStackPanel.Width - (1 - Convert.ToDouble(currentStep) / Convert.ToDouble(totalStep)) * _ctlStackPanel.Width - 20, 0, 0, 0);
                });
            }
        }

        /// <summary>
        /// 子进度更新
        /// </summary>
        /// <param name="totalStep"></param>
        /// <param name="currentStep"></param>
        /// <param name="message"></param>
        private void OnSubProcessUpdate(int totalStep, int currentStep, string message)
        {
            Logger.DebugToTag("UI", message);
            if (!this.Dispatcher.HasShutdownStarted
                && !this.Dispatcher.HasShutdownFinished)
            {
                this.Dispatcher.Invoke(() =>
                {
                    _ctlLblMessageSub.Text = "正在加载:" + message;
                    _ctlTxtBckSubMessage.Text = string.Format("{0:N0}%", (Convert.ToDouble(currentStep) / Convert.ToDouble(totalStep)) * 100);
                    _ctllblDecayStep.Content = string.Format("{0}|{1}", currentStep.ToString(), totalStep.ToString());

                    _ctlSPSub.Width = (1 - Convert.ToDouble(currentStep) / Convert.ToDouble(totalStep)) * _ctlStackPanelSub.Width;
                    _ctlCanvasSub.Margin = new Thickness(_ctlStackPanelSub.Width - (1 - Convert.ToDouble(currentStep) / Convert.ToDouble(totalStep)) * _ctlStackPanelSub.Width - 20, 0, 0, 0);
                });
            }
        }

        private void OnSubProcessMessageUpdate(string message)
        {
            Logger.DebugToTag("UI", message);
            if (!this.Dispatcher.HasShutdownStarted
                && !this.Dispatcher.HasShutdownFinished)
            {
                this.Dispatcher.Invoke(() =>
                {
                    _ctlLblMessageSub.Text = "正在加载:" + message;
                });
            }
        }
    }
}
